﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using WooCoo.Tools;

namespace WooCoo.Data.EF
{
    /// <summary>
    /// 该自动控制EF DbContext在上下文环境中的重用，并可利用DbContext的事务，以达天IUnitOfWork的效果
    /// </summary>
    public class DbContextManager : TransactionManager
    {
        private static object _lock = new object();

        private DbContext _context;
        private DbContextManager(string database, string label, Type dbContextType)
        {
            //通过IOC获取
            //string contractName = entityType.Namespace;
            //var contexInfo = WooCoo.IoC.Container.Current.GetExportedValue<IDataContextInfo>(contractName);
            //database = contexInfo.ConnectionName;
            if (string.IsNullOrEmpty(database))
            {
                _context = System.Activator.CreateInstance(dbContextType) as DbContext;
            }
            else
            {
                _context = System.Activator.CreateInstance(dbContextType, database) as DbContext;
            }
        }

        /// <summary>
        /// 获取指定的数据库上下文管理对象
        /// </summary>
        public static DbContextManager GetManager(Type entityType)
        {
            return GetManager("Database", entityType);
        }

        /// <summary>
        /// 获取指定的数据库上下文管理对象
        /// </summary>
        /// <param name="database">数据库连接配置名或连接字符串.</param>
        /// <param name="entityType">数据实体</param>
        /// <return>将返回数据实体所在的DataContext实例</return>
        public static DbContextManager GetManager(string database, Type entityType)
        {
            return GetManager(database, "default", entityType);
        }

        /// <summary>
        /// 获取指定的数据库上下文管理对象        
        /// </summary>
        /// <param name="database">
        /// 数据库连接配置名或连接字符串
        /// </param>
        /// <param name="label">标识.</param>
        /// <returns>将返回数据实体所在的DataContext实例</returns>
        public static DbContextManager GetManager(string database, string label, Type entityType)
        {
            //获取实体数据库信息
            var contexInfo = WooCoo.IoC.Container.Current.GetExportedValue<IDataContextInfo>(entityType.Namespace);
            if (contexInfo == null)
                throw Error.NotSupported("IOC容器中未找到对应的DataContext");
            database = contexInfo.ConnectionName;

            lock (_lock)
            {
                var contextLabel = GetContextName(database, label);
                DbContextManager mgr = null;
                if (ApplicationContext.LocalContext.Contains(contextLabel))
                {
                    mgr = (DbContextManager)(ApplicationContext.LocalContext[contextLabel]);
                }
                else
                {
                    mgr = new DbContextManager(database, label, contexInfo.DataContextType);
                    mgr.ContextLabel = contextLabel;
                    ApplicationContext.LocalContext[contextLabel] = mgr;
                }
                mgr.BeginTranaction();
                return mgr;
            }
        }


        private static string GetContextName(string database, string label)
        {
            return "__dbctx:" + label + "-" + database;
            //return "__transaction:" + database + "-" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString();
        }


        /// <summary>
        /// Gets the DbContext object.
        /// </summary>
        public DbContext DbContext
        {
            get
            {
                return _context;
            }
        }

        protected override void TransactionDispose()
        {
            _context.Dispose();
        }

        /// <summary>
        /// 事务提交，当引用数为1时，表示可提交事务。事务的提交将在最外层Using中
        /// </summary>
        protected override int CommitInternal()
        {
            try
            {
                _context.Configuration.ValidateOnSaveEnabled = false;
                return _context.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                string error = null;
                //foreach(var item in dbEx.)
                //{
                    
                //}
                throw dbEx;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

    }
}
